﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml.Linq;
using System.Text.RegularExpressions;
using WindowsPhone.Marketplace;

namespace MarketplaceCrawler
{
    class Program
    {
        static string _baseDirectory = @"c:\temp\marketplace";

        static MarketplaceSettings _settings = new MarketplaceSettings();

        static void Main(string[] args)
        {
            WindowsPhoneMarketplace marketplace = new WindowsPhoneMarketplace(_baseDirectory);
            MarketplaceData data = marketplace.GetMarketplaceData(new string[] { "zh-CN", "ko-KR" });

            //DownloadAll();
            CountApps(data);
            CountAppDistribution(data);
            CountAppPurchaseTypes(data);
            CountUniqueApps(data);

            System.Console.WriteLine("Finished! Press Any Key to Exit...");
            System.Console.ReadKey();
        }

        public static void DownloadAll()
        {
            Crawler c = new Crawler();

            foreach (string langCode in WindowsPhoneMarketplace.KnownLangCodes)
            {
                c.GetAllApps(_baseDirectory, langCode);
            }
        }

        public static XElement GetMarketplaceXml(string file)
        {
            using (StreamReader reader = new StreamReader(file))
            {
                string xml = reader.ReadToEnd();

                return XElement.Parse(xml);
            }
        }

        public static Dictionary<string, XElement> GetAllMarketplaceXml()
        {
            Dictionary<string, XElement> rv = new Dictionary<string, XElement>();
            string file;

            foreach (string lang in WindowsPhoneMarketplace.KnownLangCodes)
            {
                //TODO: support multiple files
                file = Path.Combine(_baseDirectory, "marketplace_") + lang + "_1" + ".xml";
                if (File.Exists(file))
                    rv.Add(lang, GetMarketplaceXml(file));
            }

            return rv;
        }

        /// <summary>
        /// Prints the number of individiual apps in each marketplace followed by a total tally
        /// </summary>
        /// <param name="data"></param>
        public static void CountApps(MarketplaceData data)
        {
            foreach (MarketplaceLang lang in data.Marketplaces.Values)
            {
                Console.WriteLine(lang.LanguageCode + ": " + lang.Apps.Count);
            }

            Console.WriteLine("Overall app count: " + data.Apps.Count);
        }

        /// <summary>
        /// Groups apps by the number of marketplaces they are in and prints the
        /// results
        /// </summary>
        /// <param name="data"></param>
        public static void CountAppDistribution(MarketplaceData data)
        {
            Dictionary<int, int> distribution = new Dictionary<int, int>();

            foreach (MarketplaceApp app in data.Apps.Values)
            {
                if (distribution.ContainsKey(app.Marketplaces.Count))
                    distribution[app.Marketplaces.Count]++;
                else
                    distribution.Add(app.Marketplaces.Count, 1);
            }

            foreach (int count in distribution.Keys)
            {
                Console.WriteLine(count + ": " + distribution[count]);
            }
        }

        /// <summary>
        /// Prints out a table of number of apps by purchase type (paid/trial/free)
        /// across the various locales
        /// </summary>
        /// <param name="data"></param>
        public static void CountAppPurchaseTypes(MarketplaceData data)
        {
            // array indexes
            int PAID  = 0;
            int TRIAL = 1;
            int FREE  = 2;

            Dictionary<string, int[]> stats = new Dictionary<string, int[]>();

            foreach (MarketplaceApp app in data.Apps.Values)
            {
                foreach (string lang in app.Offers.Keys)
                {

                    if (!stats.ContainsKey(lang))
                        stats.Add(lang, new int[3]);

                    foreach (MarketplaceOffer offer in app.Offers[lang])
                    {

                        if (offer.LicenseRight == "Trial")
                        {
                            stats[lang][TRIAL]++;
                        }
                        else if (offer.LicenseRight == "Purchase")
                        {
                            if (offer.Price > 0)
                            {
                                stats[lang][PAID]++;
                            }
                            else
                            {
                                stats[lang][FREE]++;
                            }
                        }

                        Console.WriteLine("Price: " + offer.Price + " right: " + offer.LicenseRight);
                    }
                }
            }

            foreach (string lang in stats.Keys)
            {
                Console.WriteLine("Marketplace " + lang + ": Paid Only: " + (stats[lang][PAID] - stats[lang][TRIAL]) + " Paid & Trial: " + stats[lang][TRIAL] + " Free: " + stats[lang][FREE]);
            }
            
        }

        /// <summary>
        /// Attempts to coalesce similar applications into one in order to print out
        /// a more accurate representation of the real number of apps.
        /// 
        /// Similar applications are defined as applications that have a paid and free
        /// version so are published as two applications, even though they should be
        /// counted as one.
        /// </summary>
        public static void CountUniqueApps(MarketplaceData data)
        {
            //Dictionary<string, List<XElement>> publishers = GetPublisherApps();
            List<MarketplaceApp> apps;
            List<MarketplaceApp> removals;

            MarketplaceApp curI, curJ;

            int count = 0;
            int countRemovals = 0;
            
            foreach (MarketplacePublisher publisher in data.Publishers.Values)
            {
                apps = publisher.Apps.Values.ToList();

                removals = new List<MarketplaceApp>();

                for (int i = 0; i < apps.Count; i++)
                {
                    curI = apps[i];

                    if (removals.Contains(curI))
                        continue;

                    string cleanedI = curI.Title.Replace("lite", "").Replace("free", "").Replace("trial", "").Replace("pro", "").Replace("ad supported", "").Trim();

                    for (int j = i + 1; j < apps.Count; j++) {

                        curJ = apps[j];

                        if (apps[i] == apps[j])
                            continue;

                        string cleanedJ = curJ.Title.Replace("lite", "").Replace("free", "").Replace("trial", "").Replace("pro", "").Replace("ad supported", "").Trim();

                        // the contains will eliminate duplicate entries of an app due to publishing across multiple
                        // marketplaces and should also eliminate most of the Lite / Free combinations with minimal
                        // mistakes
                        if (curI.Id == curJ.Id)
                        {
                            throw new Exception("same id? Shouldn't actually happen");
                        }
                        else if (curI.Title.Contains(curJ.Title))
                        {
                            System.Console.WriteLine("Queing " + curI.Title + " for removal (matches: " + curJ.Title + ")");

                            removals.Add(apps[i]);
                            break;
                        }
                        else if (cleanedI == cleanedJ)
                        {
                            System.Console.WriteLine("Queing " + curI.Title + " for removal (matches: " + curJ.Title + ")");

                            removals.Add(apps[i]);
                            break;
                        }
                    }
                }

                countRemovals += removals.Count;

                foreach (MarketplaceApp app in removals)
                    apps.Remove(app);

                count += apps.Count;
            }

            System.Console.WriteLine("\nFinal Apps Count: " + count + " (removed: " + countRemovals + ")\n\n\n");
        }

    }
}
